package web.graficos.base;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import logic.Buscador;
import bean.juego.Juego;
import bean.jugador.Jugador;
import util.Pair;
import web.graficos.Graficos;
import web.util.GeneradorEstadisticas;
import web.util.GeneradorEstadisticasPorCantidad;
import web.util.ProcesadorJuegos;
import web.util.ProcesadorJuegosPorCantidad;

public class GraficosBase implements Graficos {

    private List<Juego> listaJuegos;
    private static final Logger logger = Logger.getLogger(GraficosBase.class.getName());

    public static GraficosBase getInstancia() {
        return new GraficosBase();
    }

    GraficosBase() {
        this.listaJuegos = Buscador.getListaJuegos();
    }

    @Override
    public String nombresJugadoresCSV(List<Jugador> jugadores) {
        StringBuilder sb = new StringBuilder();

        for (Jugador jugador : jugadores) {
            if (sb.length() > 0) {
                sb.append(',');
            }
            sb.append('\'').append(jugador.getDatos().getNombre()).append('\'');
        }

        return sb.toString();
    }

    @Override
    public String matrixEfectividad(List<Jugador> jugadores) {
        ProcesadorJuegos procesador = new ProcesadorJuegos() {

            private Map<Jugador, Pair<Integer, Integer>> map = new HashMap<Jugador, Pair<Integer, Integer>>();

            @Override
            public void init(List<Jugador> jugadores) {
                for (Jugador jugador : jugadores) {
                    this.map.put(jugador, new Pair<Integer, Integer>(0, 0));
                }
            }

            @Override
            public void procesarJuego(Juego juego, Jugador jugador) {
                if (juego.esJugador(jugador)) {
                    Pair<Integer, Integer> pair = this.map.get(jugador);
                    if (juego.esGanador(jugador)) {
                        this.map.put(jugador, new Pair<Integer, Integer>(pair.getFirst() + 1, pair.getSecond() + 1));
                    } else {
                        this.map.put(jugador, new Pair<Integer, Integer>(pair.getFirst(), pair.getSecond() + 1));
                    }
                }
            }

            @Override
            public Object getDato(Jugador jugador) {
                Pair<Integer, Integer> pair = this.map.get(jugador);
                int partidosJugados = pair.getSecond();
                int partidosGanados = pair.getFirst();

                float efectividad;
                if (partidosJugados > 0) {
                    efectividad = (float)100 * partidosGanados / partidosJugados;
                } else {
                    efectividad = 0;
                }

                return Float.valueOf(efectividad);
            }
        };

        GeneradorEstadisticas generador = new GeneradorEstadisticas(procesador);
        String retorno = generador.procesar(jugadores, this.listaJuegos);

        return retorno;
    }

    @Override
    public String matrixEfectividadPonderada(List<Jugador> jugadores) {
        final int minimoPartidos = 15;

        ProcesadorJuegos procesador = new ProcesadorJuegos() {

            private Map<Jugador, Pair<Integer, Integer>> map = new HashMap<Jugador, Pair<Integer, Integer>>();

            @Override
            public void init(List<Jugador> jugadores) {
                for (Jugador jugador : jugadores) {
                    this.map.put(jugador, new Pair<Integer, Integer>(0, 0));
                }
            }

            @Override
            public void procesarJuego(Juego juego, Jugador jugador) {
                if (juego.esJugador(jugador)) {
                    Pair<Integer, Integer> pair = this.map.get(jugador);
                    if (juego.esGanador(jugador)) {
                        this.map.put(jugador, new Pair<Integer, Integer>(pair.getFirst() + 1, pair.getSecond() + 1));
                    } else {
                        this.map.put(jugador, new Pair<Integer, Integer>(pair.getFirst(), pair.getSecond() + 1));
                    }
                }
            }

            @Override
            public Object getDato(Jugador jugador) {
                Pair<Integer, Integer> pair = this.map.get(jugador);
                int partidosJugados = pair.getSecond();
                int partidosGanados = pair.getFirst();

                float efectividadJugador;
                if (partidosJugados >= minimoPartidos) {
                    efectividadJugador = (float) partidosGanados / partidosJugados;
                } else {
                    efectividadJugador = 0;
                }

                float efectividadMaxima = 0;
                float efectividadMinima = 1;
                for (Pair<Integer, Integer> pair2 : this.map.values()) {
                    float efectividadActual;

                    partidosJugados = pair2.getSecond();
                    partidosGanados = pair2.getFirst();

                    if (partidosJugados >= minimoPartidos) {
                        efectividadActual = (float) partidosGanados / partidosJugados;
                    } else {
                        efectividadActual = 0;
                    }

                    efectividadMaxima = Math.max(efectividadActual, efectividadMaxima);
                    if (efectividadActual > 0) {
                        efectividadMinima = Math.min(efectividadActual, efectividadMinima);
                    }
                }
                if (efectividadJugador == 0) {
                    efectividadJugador = efectividadMinima;
                }
                efectividadJugador = (efectividadJugador - efectividadMinima) / (efectividadMaxima - efectividadMinima);
                return Float.valueOf(efectividadJugador);
            }
        };

        GeneradorEstadisticas generador = new GeneradorEstadisticas(procesador);
        String retorno = generador.procesar(jugadores, this.listaJuegos);

        return retorno;
    }

    @Override
    public String matrixPuntosPorMinuto(List<Jugador> jugadores) {

        ProcesadorJuegos procesador = new ProcesadorJuegos() {

            private Map<Jugador, Pair<Integer, Integer>> map = new HashMap<Jugador, Pair<Integer, Integer>>();

            @Override
            public void init(List<Jugador> jugadores) {
                for (Jugador jugador : jugadores) {
                    this.map.put(jugador, new Pair<Integer, Integer>(0, 0));
                }
            }

            @Override
            public void procesarJuego(Juego juego, Jugador jugador) {
                if (juego.esJugador(jugador)) {
                    Pair<Integer, Integer> pair = this.map.get(jugador);

                    this.map.put(jugador, new Pair<Integer, Integer>(
                            pair.getFirst() + juego.getMapaPuntosJugador().get(jugador),
                            pair.getSecond() + juego.getTiempoEnSegundos()));

                }
            }

            @Override
            public Object getDato(Jugador jugador) {
                Pair<Integer, Integer> pair = this.map.get(jugador);
                int segundosJugados = pair.getSecond();
                int puntosAcumulados = pair.getFirst();

                float puntosPorMinuto;
                if (segundosJugados > 0) {
                    puntosPorMinuto = (float) 60 * puntosAcumulados / segundosJugados;
                } else {
                    puntosPorMinuto = 0;
                }

                return puntosPorMinuto;
            }
        };

        GeneradorEstadisticas generador = new GeneradorEstadisticas(procesador);
        String retorno = generador.procesar(jugadores, this.listaJuegos);

        return retorno;
    }

    @Override
    public String getDeathKillsRatio(List<Jugador> jugadores) {

        ProcesadorJuegos procesador = new ProcesadorJuegos() {

            private Map<Jugador, Pair<Integer, Integer>> map = new HashMap<Jugador, Pair<Integer, Integer>>();

            @Override
            public void init(List<Jugador> jugadores) {
                for (Jugador jugador : jugadores) {
                    this.map.put(jugador, new Pair<Integer, Integer>(0, 0));
                }
            }

            @Override
            public void procesarJuego(Juego juego, Jugador jugador) {
                if (juego.esJugador(jugador)) {
                    Pair<Integer, Integer> pair = this.map.get(jugador);
                    this.map.put(jugador, new Pair<Integer, Integer>(
                            pair.getFirst() + juego.getMuertesHechas(jugador),
                            pair.getSecond() + juego.getMuertesRecibidas(jugador)));

                }
            }

            @Override
            public Object getDato(Jugador jugador) {
                Pair<Integer, Integer> pair = this.map.get(jugador);

                int deaths = pair.getSecond();
                int kills = pair.getFirst();

                float efectividad;
                if (deaths > 0) {
                    efectividad = (float) kills / deaths;
                } else {
                    efectividad = 0;
                }

                return efectividad;
            }
        };

        GeneradorEstadisticas generador = new GeneradorEstadisticas(procesador);
        String retorno = generador.procesar(jugadores, this.listaJuegos);

        return retorno;
    }

    @Override
    public String matrixPuntosPorMinutoPorPartido(List<Jugador> jugadores) {

        ProcesadorJuegos procesador = new ProcesadorJuegos() {

            private Map<Jugador, Pair<Integer, Integer>> map = new HashMap<Jugador, Pair<Integer, Integer>>();

            @Override
            public void init(List<Jugador> jugadores) {
                for (Jugador jugador : jugadores) {
                    this.map.put(jugador, new Pair<Integer, Integer>(0, 0));
                }
            }

            @Override
            public void procesarJuego(Juego juego, Jugador jugador) {
                if (juego.esJugador(jugador)) {
                    this.map.put(jugador, new Pair<Integer, Integer>(
                            juego.getMapaPuntosJugador().get(jugador),
                            juego.getTiempoEnSegundos()));

                }
            }

            @Override
            public Object getDato(Jugador jugador) {
                Pair<Integer, Integer> pair = this.map.get(jugador);
                int segundosJugados = pair.getSecond();
                int puntosAcumulados = pair.getFirst();

                float puntosPorMinuto;
                if (segundosJugados > 0) {
                    puntosPorMinuto = (float) 60 * puntosAcumulados / segundosJugados;
                } else {
                    puntosPorMinuto = 0;
                }

                return puntosPorMinuto;
            }
        };

        GeneradorEstadisticas generador = new GeneradorEstadisticas(procesador);
        String retorno = generador.procesar(jugadores, this.listaJuegos);

        return retorno;
    }

    @Override
    public String matrixHistoricoPuntos(Juego juego, int granularidad) {
        StringBuilder sb = new StringBuilder();
        List<Jugador> jugadores = juego.getJugadores();

        List<Map<Jugador, Integer>> puntosTiempo = juego.getPuntosJugadorTiempo();

        int i = 0;
        for (Map<Jugador, Integer> map : puntosTiempo) {
            if (i % granularidad == 0) {
                sb.append("['").append(i / granularidad).append("',");
                for (Jugador jugador : jugadores) {
                    sb.append(map.get(jugador));
                    sb.append(',');
                }
                sb.append("],");
            }
            i++;
        }

        return sb.toString();
    }

    @Override
    public String matrixPuntosPorMinuto(List<Jugador> jugadores, int cantidad) {

        ProcesadorJuegosPorCantidad procesador = new ProcesadorJuegosPorCantidad() {

            private int segundosJugados;
            private int puntosAcumulados;
            private Jugador jugador;

            @Override
            public void init(Jugador jugador) {
                this.segundosJugados = 0;
                this.puntosAcumulados = 0;
                this.jugador = jugador;
            }

            @Override
            public void procesarJuego(Juego juego) {
                this.segundosJugados += juego.getTiempoEnSegundos();
                this.puntosAcumulados += juego.getMapaPuntosJugador().get(this.jugador);
            }

            @Override
            public Object getDato() {
                float puntosPorMinuto;
                if (this.segundosJugados > 0) {
                    puntosPorMinuto = (float) 60 * this.puntosAcumulados / this.segundosJugados;
                } else {
                    puntosPorMinuto = 0;
                }

                return puntosPorMinuto;
            }
        };

        GeneradorEstadisticasPorCantidad generador = new GeneradorEstadisticasPorCantidad(procesador);
        String retorno = generador.procesar(jugadores, this.listaJuegos, cantidad);

        return retorno;
    }

    @Override
    public String matrixEfectividad(List<Jugador> jugadores, int cantidad) {

        ProcesadorJuegosPorCantidad procesador = new ProcesadorJuegosPorCantidad() {

            private int partidosJugados;
            private int partidosGanados;
            private Jugador jugador;

            @Override
            public void init(Jugador jugador) {
                this.partidosJugados = 0;
                this.partidosGanados = 0;
                this.jugador = jugador;
            }

            @Override
            public void procesarJuego(Juego juego) {
                this.partidosJugados++;
                if (juego.esGanador(this.jugador)) {
                    this.partidosGanados++;
                }
            }

            @Override
            public Object getDato() {
                float efectividad;
                if (this.partidosJugados > 0) {
                    efectividad = (float) 100 * this.partidosGanados / this.partidosJugados;
                } else {
                    efectividad = 0;
                }

                return efectividad;
            }
        };

        GeneradorEstadisticasPorCantidad generador = new GeneradorEstadisticasPorCantidad(procesador);
        String retorno = generador.procesar(jugadores, this.listaJuegos, cantidad);

        return retorno;
    }

    @Override
    public String getDeathKillsRatio(List<Jugador> jugadores, int cantidad) {

        ProcesadorJuegosPorCantidad procesador = new ProcesadorJuegosPorCantidad() {

            private int deaths;
            private int kills;
            private Jugador jugador;

            @Override
            public void init(Jugador jugador) {
                this.deaths = 0;
                this.kills = 0;
                this.jugador = jugador;
            }

            @Override
            public void procesarJuego(Juego juego) {
                this.kills += juego.getMuertesHechas(this.jugador);
                this.deaths += juego.getMuertesRecibidas(this.jugador);
            }

            @Override
            public Object getDato() {
                float ratio;
                if (this.deaths > 0) {
                    ratio = (float) this.kills / this.deaths;
                } else {
                    ratio = 0;
                }

                return ratio;
            }
        };

        GeneradorEstadisticasPorCantidad generador = new GeneradorEstadisticasPorCantidad(procesador);
        String retorno = generador.procesar(jugadores, this.listaJuegos, cantidad);

        return retorno;
    }

    @Override
    public String matrixVersusJugadores(final Jugador jugador1, final Jugador jugador2, final List<Juego> juegos) {
        StringBuilder sb = new StringBuilder();
		int cantidad = 0;
        int i = 0;
		for (Juego juego : juegos) {
            i++;
            cantidad+=juego.getMuertesJugadorAJugador(jugador1, jugador2);
            cantidad-=juego.getMuertesJugadorAJugador(jugador2, jugador1);
            sb.append("[").append(i).append(",").append(cantidad).append("],");
		}

        return sb.toString();
    }
}
